/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.kongzue.baseokhttpv3.slice;

import ohos.agp.components.AttrSet;
import ohos.agp.components.DependentLayout;
import ohos.agp.render.Arc;
import ohos.agp.render.Paint;
import ohos.agp.render.Paint.Style;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;

/**
 * ProgressBar.
 *
 * @author Todd Davies
 * <p/>
 * See MIT-LICENSE.txt for licence details
 */
public class ProgressWheel extends DependentLayout {
    //Sizes (with defaults)
    private int layoutHeight = 0;
    private int layoutWidth = 0;
    private int barLength = 60;
    private int barWidth = 20;
    private int rimWidth = 20;
    private int textSize = 20;
    private float contourSize = 0;

    //Padding (with defaults)
    private int paddingTop = 5;
    private int paddingBottom = 5;
    private int paddingLeft = 5;
    private int paddingRight = 5;

    //Colors (with defaults)
    private int barColor = 0xAA000000;
    private int contourColor = 0x9900ff;
    private int circleColor = 0x00000000;
    private int rimColor = 0xAADDDDDD;
    private int textColor = 0xFF000000;

    //Paints
    private Paint barPaint = new Paint();
    private Paint circlePaint = new Paint();
    private Paint rimPaint = new Paint();
    private Paint textPaint = new Paint();
    private Paint contourPaint = new Paint();


    //Rectangles
    private RectFloat innerCircleBounds = new RectFloat();
    private RectFloat circleBounds = new RectFloat();
    private RectFloat circleOuterContour = new RectFloat();

    //Animation
    //The amount of pixels to move the bar by on each draw
    private float spinSpeed = 2f;
    //The number of milliseconds to wait in between each draw
    private int delayMillis = 100;
    private float progress = 0;
    private boolean isSpinning = false;

    //Other
    private String text = "";
    private String[] splitText = {};


    private EventHandler spinHandler = new EventHandler(EventRunner.getMainEventRunner()) {
        @Override
        public void processEvent(InnerEvent msg) {
            invalidate();
            if (isSpinning) {
                spinHandler.sendEvent(0, delayMillis);
            }
            super.processEvent(msg);
        }
    };


    public ProgressWheel(Context context, AttrSet attrSet) {
        super(context, attrSet);
        barLength = TypedAttrUtils.getDimensionPixelSize(attrSet, "barLength", 0);
        barWidth = TypedAttrUtils.getDimensionPixelSize(attrSet, "barWidth", 0);
        rimWidth = TypedAttrUtils.getDimensionPixelSize(attrSet, "rimWidth", 0);
        spinSpeed = TypedAttrUtils.getDimensionPixelSize(attrSet, "spinSpeed", 0);
        barColor = TypedAttrUtils.getIntColor(attrSet, "barColor", 0);
        rimColor = TypedAttrUtils.getIntColor(attrSet, "rimColor", 0);
        init();
    }

    /**
     * The constructor for the ProgressWheel
     */
    private void init() {
        //Draw the text (attempts to center it horizontally and vertically)
        DrawTask task = (component, canvas) -> {
            setupBounds();
            setupPaints();
            canvas.drawArc(innerCircleBounds, new Arc(360, 360, false), circlePaint);
            canvas.drawArc(circleBounds, new Arc(360, 360, false), rimPaint);
            canvas.drawArc(circleOuterContour, new Arc(360, 360, false), contourPaint);
            if (isSpinning) {
                canvas.drawArc(circleBounds, new Arc(progress - 90, barLength, false), barPaint);
            } else {
                canvas.drawArc(circleBounds, new Arc(-90, progress, false), barPaint);
            }
            //Draw the text (attempts to center it horizontally and vertically)
            float textHeight = textPaint.descent() - textPaint.ascent();
            float verticalTextOffset = (textHeight / 2) - textPaint.descent();

            for (String line : splitText) {
                float horizontalTextOffset = textPaint.measureText(line) / 2;
                canvas.drawText(
                        textPaint,
                        line,
                        (float) component.getWidth() / 2 - horizontalTextOffset,
                        (float) component.getHeight() / 2 + verticalTextOffset);
            }
            if (isSpinning) {
                scheduleRedraw();
            }
        };
        addDrawTask(task);
    }

    /**
     * Set the properties of the paints we're using to
     * draw the progress wheel
     */
    private void setupPaints() {
        barPaint.setColor(new Color(barColor));
        barPaint.setAntiAlias(true);
        barPaint.setStyle(Style.STROKE_STYLE);
        barPaint.setStrokeWidth(barWidth);

        rimPaint.setColor(new Color(rimColor));
        rimPaint.setAntiAlias(true);
        rimPaint.setStyle(Style.STROKE_STYLE);
        rimPaint.setStrokeWidth(rimWidth);

        circlePaint.setColor(new Color(circleColor));
        circlePaint.setAntiAlias(true);
        circlePaint.setStyle(Style.FILL_STYLE);

        textPaint.setColor(new Color(textColor));
        textPaint.setStyle(Style.FILL_STYLE);
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(textSize);

        contourPaint.setColor(new Color(contourColor));
        contourPaint.setAntiAlias(true);
        contourPaint.setStyle(Style.STROKE_STYLE);
        contourPaint.setStrokeWidth(contourSize);
    }

    /**
     * Set the bounds of the component
     */
    private void setupBounds() {
        // Width should equal to Height, find the min value to setup the circle
        int minValue = Math.min(layoutWidth, layoutHeight);

        // Calc the Offset if needed
        int xOffset = layoutWidth - minValue;
        int yOffset = layoutHeight - minValue;

        // Add the offset
        paddingTop = this.getPaddingTop() + (yOffset / 2);
        paddingBottom = this.getPaddingBottom() + (yOffset / 2);
        paddingLeft = this.getPaddingLeft() + (xOffset / 2);
        paddingRight = this.getPaddingRight() + (xOffset / 2);

        int width = getWidth();
        int height = getHeight();


        innerCircleBounds = new RectFloat(
                paddingLeft + (1.5f * barWidth),
                paddingTop + (1.5f * barWidth),
                width - paddingRight - (1.5f * barWidth),
                height - paddingBottom - (1.5f * barWidth));
        circleBounds = new RectFloat(
                paddingLeft + barWidth,
                paddingTop + barWidth,
                width - paddingRight - barWidth,
                height - paddingBottom - barWidth);
        circleOuterContour = new RectFloat(
                circleBounds.left - (rimWidth / 2.0f) - (contourSize / 2.0f),
                circleBounds.top - (rimWidth / 2.0f) - (contourSize / 2.0f),
                circleBounds.right + (rimWidth / 2.0f) + (contourSize / 2.0f),
                circleBounds.bottom + (rimWidth / 2.0f) + (contourSize / 2.0f));
    }

    @Override
    public void addDrawTask(DrawTask drawTask) {
        super.addDrawTask(drawTask);
        drawTask.onDraw(this, mCanvasForTaskOverContent);

    }

    private void scheduleRedraw() {
        progress += spinSpeed;
        if (progress > 360) {
            progress = 0;
        }
        invalidate();
    }

    /**
     * Check if the wheel is currently spinning
     *
     * @return isSpinning
     */
    public boolean isSpinning() {
        return isSpinning;
    }

    /**
     * Reset the count (in increment mode)
     */
    public void resetCount() {
        progress = 0;
        setText("0%");
        invalidate();
    }

    /**
     * Turn off startSpinning mode
     */
    public void stopSpinning() {
        isSpinning = false;
        progress = 0;
        invalidate();
    }


    /**
     * Puts the view on spin mode
     */
    public void startSpinning() {
        isSpinning = true;
        spinHandler.sendEvent(0);
    }

    /**
     * Increment the progress by 1 (of 360)
     */
    public void incrementProgress() {
        incrementProgress(1);
    }

    public void incrementProgress(int amount) {
        isSpinning = false;
        progress += amount;
        if (progress > 360) {
            progress %= 360;
        }
        invalidate();
    }

    /**
     * Set the text in the progress bar
     * Doesn't invalidate the view
     *
     * @param text the text to show ('\n' constitutes a new line)
     */
    public void setText(String text) {
        this.text = text;
        splitText = this.text.split("\n");
    }

    /**
     * getPaddingTop
     *
     * @return int
     */
    public int getPaddingTop() {
        return paddingTop;
    }

    /**
     * setPaddingTop
     *
     * @param paddingTop paddingTop
     */
    public void setPaddingTop(int paddingTop) {
        this.paddingTop = paddingTop;
    }

    /**
     * getPaddingBottom
     *
     * @return paddingBottom
     */
    public int getPaddingBottom() {
        return paddingBottom;
    }

    /**
     * setPaddingBottom
     *
     * @param paddingBottom paddingBottom
     */
    public void setPaddingBottom(int paddingBottom) {
        this.paddingBottom = paddingBottom;
    }

    /**
     * getPaddingLeft
     *
     * @return int
     */
    public int getPaddingLeft() {
        return paddingLeft;
    }

    /**
     * setPaddingLeft
     *
     * @param paddingLeft paddingLeft
     */
    public void setPaddingLeft(int paddingLeft) {
        this.paddingLeft = paddingLeft;
    }

    /**
     * getPaddingRight
     *
     * @return int
     */
    public int getPaddingRight() {
        return paddingRight;
    }

    /**
     * setPaddingRight
     *
     * @param paddingRight paddingRight
     */
    public void setPaddingRight(int paddingRight) {
        this.paddingRight = paddingRight;
    }

}